package core

import (
	"bytes"
	"encoding/binary"
	jsoniter "github.com/json-iterator/go"
	"log"
	"os"
	"sync"
	"time"
)

var SPLIT = []byte("\037")
var logChannel = make(chan []byte, 16)
var FLAG_WRITE_LOG = false
var CHECK_LOG = []byte("\035\036")

type Log struct {
	Action         int         `json:"a"`
	DBName         string      `json:"d"`
	CollectionName string      `json:"c"`
	Value          interface{} `json:"v"`
}

func InitLog() {
	//读取
	readLogHandler()

	//开始写入日志
	FLAG_WRITE_LOG = true

	go writeLogHandler()

	go time.AfterFunc(time.Duration(10)*time.Second, checkLog)
}

func checkLog() {
	logChannel <- CHECK_LOG
	time.AfterFunc(time.Duration(ScanStep), checkLog)
}

func handleLog(log *Log) {
	switch log.Action {
	case ACTION_SET:
		setMany(log.DBName, log.CollectionName, log.Value)
		break
	case ACTION_DELETE:
		deleteByIds(log.DBName, log.CollectionName, log.Value)
		break

	case ACTION_DROP_DB:
		DropDB(log.DBName, false)
		break
	}
}

func readLogHandler() {
	var wg sync.WaitGroup
	wg.Add(16)
	for _i := 0; _i < 16; _i++ {
		var i = _i
		go func() {
			defer wg.Done()
			fi, err := os.Open(DBPath + "/log")
			if err != nil {
				return
			}
			defer fi.Close()
			stat, err := fi.Stat()
			if err != nil {
				return
			}
			var flag []byte = make([]byte, 1)
			var lenbs []byte = make([]byte, 8)
			var len int64
			var pos int64
			for pos < stat.Size() {
				if int(pos%4096) == i {
					fi.ReadAt(flag, pos)
					if flag[0] == SPLIT[0] {
						fi.ReadAt(lenbs, pos+1)
						len = BytesToInt64(lenbs)
						bs := make([]byte, len)
						fi.ReadAt(bs, pos+9)
						var log Log
						err := jsoniter.Unmarshal(bs, &log)
						if err == nil {
							handleLog(&log)
						}
					}
				}
				pos += Max((len/4096+1)*4096, 4096)
			}
		}()

	}
	wg.Wait()
}

func writeLogHandler() {
	fi, err := os.OpenFile(DBPath+"/log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
	if err != nil {
		log.Fatal("打开日志文件失败")
	}
	//stat, err := fi.Stat()
	//if err != nil {
	//	log.Fatal("打开日志文件失败")
	//}
	//var pos int64 = (stat.Size()/4096 + 1) * 4096
	var buffer = new(bytes.Buffer)
	defer fi.Close()
	for {
		select {
		case bs := <-logChannel:
			if bytes.Equal(bs, CHECK_LOG) {
				func() {
					stat, err := fi.Stat()
					if err != nil {
						return
					}
					if uint64(stat.Size()) < MaxLogSize {
						return
					}
					globalMutex.Lock()
					defer globalMutex.Unlock()
					err = Snapshot(false)
					if err != nil {
						return
					}
					fi.Close()
					os.Remove(DBPath + "/log")
					fi, err := os.OpenFile(DBPath+"/log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
					if err != nil {
						return
					}
					if true {
						return
					}
					//not run
					fi.Sync()
				}()
			} else {
				l := int64(len(bs))
				buffer.Reset()
				buffer.Write(SPLIT)
				buffer.Write(Int64ToBytes(l))
				buffer.Write(bs)

				//fi.Seek(pos, io.SeekStart)
				//fi.Write(SPLIT)
				//fi.Write(Int64ToBytes(l))
				//fi.Write(bs)
				max := Max(4096*(l/4096+1), 4096)
				var i int64
				for i = int64(len(bs) + 9); i < max; i++ {
					buffer.WriteByte(0x00)
				}
				//fi.Seek(0, os.SEEK_END)
				buffer.WriteTo(fi)

				fi.Sync()
				//fi.WriteAt(buffer.Bytes(), )
				//pos += max
			}
		}
	}

}

func WriteLog(db, col string, action int, item interface{}) {
	if !FLAG_WRITE_LOG {
		return
	}
	//go func() {
	var log = &Log{
		Action:         action,
		DBName:         db,
		CollectionName: col,
		Value:          item,
	}
	bs, err := jsoniter.Marshal(log)
	if err != nil {
		return
	}
	logChannel <- bs
	//}()
}

func Int64ToBytes(i int64) []byte {
	var buf = make([]byte, 8)
	binary.BigEndian.PutUint64(buf, uint64(i))
	return buf
}

func BytesToInt64(buf []byte) int64 {
	return int64(binary.BigEndian.Uint64(buf))
}

func Min(x, y int) int {
	if x < y {
		return x
	}
	return y
}
func Max(x, y int64) int64 {
	if x > y {
		return x
	}
	return y
}
